package top.easyblog.core.support.lock;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import top.easyblog.core.util.AssertUtil;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: frank.huang
 * @date: 2023-12-21 21:58
 */
@Component
public class RedisDistributeLockFactory {

    //锁超时等待时间
    @Value("${custom.dlock.lock-expire-time-ms:60000}")
    private long lockExpireTimeMs;

    //获取锁等待时间
    @Value("${custom.dlock.lock-waiting-time-ms:60000}")
    private long lockWaitingTimeMs;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    //锁实例池
    private static final Map<String, DistributedLock> LOCKS = new ConcurrentHashMap<>(1024);


    /**
     * 获取分布式锁,失败后会尝试等待锁
     *
     * @param key
     * @return
     */
    public DistributedLock getDistributedLock(String key) {
        AssertUtil.isTrue(StringUtils.isNotBlank(key), "Required non-blank param 'key'");
        DistributedLock lock = LOCKS.get(key);
        if (lock == null) {
            synchronized (this) {
                lock = LOCKS.get(key);
                if (lock == null) {
                    lock = new RedisDistributedLock(key, redisTemplate, lockExpireTimeMs, lockWaitingTimeMs);
                    LOCKS.putIfAbsent(key, lock);
                }
            }
        }
        return lock;
    }


    /**
     * 获取分布式锁,失败后不会等待锁
     *
     * @param key
     * @return
     */
    public DistributedLock getNonWaitingDistributedLock(String key) {
        AssertUtil.isTrue(StringUtils.isNotBlank(key), "Required non-blank param 'key'");
        DistributedLock lock = LOCKS.get(key);
        if (lock == null) {
            synchronized (this) {
                lock = LOCKS.get(key);
                if (lock == null) {
                    // 底层RedisDistributedLock，一轮尝试获取锁的时间是100ms，如果尝试一次没有获取到锁，则直接失败，相当于无等待
                    lock = new RedisDistributedLock(key, redisTemplate, lockExpireTimeMs, 100);
                    LOCKS.putIfAbsent(key, lock);
                }
            }
        }
        return lock;
    }

}
